package view

import "fmt"

// field_info {
// u2 access_flags;
// u2 name_index;
// u2 descriptor_index;
// u2 attributes_count;
// attribute_info attributes[attributes_count];
// }
const (
	ACC_PUBLIC    = 0x0001
	ACC_PRIVATE   = 0x0002
	ACC_PROTECTED = 0x0004
	ACC_STATIC    = 0x0008
	ACC_FINAL     = 0x0010
	ACC_VOLATILE  = 0x0040
	ACC_TRANSIENT = 0x0080
	ACC_SYNTHETIC = 0x1000
	ACC_ENUM      = 0x4000
)

type MemberInfo struct {
	AccessFlags     uint16
	NameIndex       uint16
	DescriptorIndex uint16
	AttributeNum    uint16
	Name            Constant
	Descriptor      Constant
	AttributeList   []AttributeInfo
}

/*
*
u2 fields_count;
field_info fields[fields_count];
*/
func ReadMemberInfo(cr *ClassReader, cp ConstantPoolInfo) []MemberInfo {

	count := cr.readUInt16(cr.readUInt8s(2))
	if count > 0 {
		fields := make([]MemberInfo, count)
		for i := 0; i < int(count); i++ {
			fields[i] = NewMemberInfo(cr, cp)
		}
		return fields
	}
	return nil
}

func NewMemberInfo(cr *ClassReader, cp ConstantPoolInfo) MemberInfo {
	member := &MemberInfo{}
	member.AccessFlags = cr.readUInt16(cr.readUInt8s(2))
	member.NameIndex = cr.readUInt16(cr.readUInt8s(2))
	member.DescriptorIndex = cr.readUInt16(cr.readUInt8s(2))
	member.AttributeNum = cr.readUInt16(cr.readUInt8s(2))
	member.Name = cp.ConstantList[member.NameIndex-1]
	member.Descriptor = cp.ConstantList[member.DescriptorIndex-1]
	if member.AttributeNum > 0 {
		member.AttributeList = make([]AttributeInfo, member.AttributeNum)
		for i := 0; i < int(member.AttributeNum); i++ {
			member.AttributeList[i] = NewAttributeInfo(cr, cp)
		}
	}
	return *member
}

type AttributeInfo interface {
	readAttrInfo(cp ConstantPoolInfo) AttributeInfo
}

type Attribute struct {
	AttributeNameIndex uint16
	AttributeLength    uint32
	AttributeName      Constant_Utf8
	Data               *AttributeData
}

func (self *Attribute) readAttrInfo(cp ConstantPoolInfo) AttributeInfo {
	var attrName = self.AttributeName.Str
	switch attrName {
	case "Code":
		return (&CodeAttr{Attribute: *self}).readAttrInfo(cp)
	case "ConstantValue":
		return (&ConstantValueAttr{Attribute: *self}).readAttrInfo(cp)
	case "LineNumberTable":
		return (&LineNumberTableAttr{Attribute: *self}).readAttrInfo(cp)
	case "LocalVariableTable":
		return (&LocalVariableTableAttr{Attribute: *self}).readAttrInfo(cp)
	case "StackMapTable":
		//?
		return (&StackMapTableAttr{Attribute: *self}).readAttrInfo(cp)
	case "Exceptions":
		return (&ExceptionTableAttr{Attribute: *self}).readAttrInfo(cp)
	case "LocalVariableTypeTable":
		return (&LocalVariableTypeTableAttr{Attribute: *self}).readAttrInfo(cp)
	case "Signature":
		return (&SignatureAttr{Attribute: *self}).readAttrInfo(cp)
	case "SourceFile":
		return (&SourceFileAttr{Attribute: *self}).readAttrInfo(cp)
	case "RuntimeVisibleAnnotations":
		//?
		return (&RuntimeVisibleAnnotationsAttr{Attribute: *self}).readAttrInfo(cp)
	case "InnerClasses":
		//?
		return (&InnerClassesAttr{Attribute: *self}).readAttrInfo(cp)
	case "BootstrapMethods":
		//?
		return (&BootstrapMethodsAttr{Attribute: *self}).readAttrInfo(cp)
	default:
		fmt.Println("属性不支持1:" + attrName)
		panic("属性不支持:" + attrName)
	}
}

type AttributeData struct {
	data []uint8
	pos  int
}

func NewAttributeInfo(cr *ClassReader, cp ConstantPoolInfo) AttributeInfo {
	attr := &Attribute{}
	attr.AttributeNameIndex = cr.readUInt16(cr.readUInt8s(2))
	attr.AttributeLength = cr.readUInt32(cr.readUInt8s(4))
	c := cp.ConstantList[attr.AttributeNameIndex-1]
	c1, ok := c.(*Constant_Utf8)
	if !ok {
		panic("类型转换异常:属性=>Constant_Utf8")
	}
	attr.AttributeName = *c1
	attr.Data = &AttributeData{
		data: cr.readUInt8s2(attr.AttributeLength), pos: 0,
	}
	return attr.readAttrInfo(cp)
}

func NewAttributeInfoFromBytes(cr *AttributeData, cp ConstantPoolInfo) AttributeInfo {
	attr := &Attribute{}
	attr.AttributeNameIndex = cr.readUInt16()
	attr.AttributeLength = cr.readUInt32()
	c := cp.ConstantList[attr.AttributeNameIndex-1]
	c1, ok := c.(*Constant_Utf8)
	if !ok {
		panic("类型转换异常:属性=>Constant_Utf8")
	}
	attr.AttributeName = *c1
	attr.Data = &AttributeData{
		data: cr.readUInt8s(int(attr.AttributeLength)), pos: 0,
	}
	return attr.readAttrInfo(cp)
}
